package com.subtlman.logframework.aop;

import com.subtlman.logframework.resp.InformResp;
import com.subtlman.logframework.resp.MyHandlerExceptionResolver;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Create: IntelliJ IDEA.
 * @Author: subtlman_ljx
 * @Date: 2022/11/12/1:40
 * @Description:
 */
@Aspect
@Component
public class AuditLogAspect {
    private static final Logger log = LoggerFactory.getLogger("AuditLogAspect");

    public AuditLogAspect() {
    }

    @Pointcut("@annotation(com.subtlman.logframework.aop.AuditLog)")
    public void logPointCut() {
    }

//    @AfterReturning(
//            pointcut = "logPointCut()"
//    )
//    public void doAfterReturning(JoinPoint joinPoint) {
//        this.handleAfterLog(joinPoint);
//    }
//
//    @Before(value = "logPointCut()")
//    public void doBefore(JoinPoint joinPoint) {
//        this.handleBeforeLog(joinPoint);
//    }
//
//    @AfterThrowing(
//            value = "logPointCut()",
//            throwing = "e"
//    )
//    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
//        this.handleAfterThrowingLog(joinPoint, e);
//    }

    @Around(value = "logPointCut()")
    private Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

            String requestURI = request.getRequestURI();
            String requestMethod = request.getMethod();
            String remoteAddr = request.getRemoteAddr();

            // 步入时间戳
            Long startTime = System.currentTimeMillis();
            MethodSignature method = (MethodSignature) proceedingJoinPoint.getSignature();
            Class<?> currentClass = proceedingJoinPoint.getTarget().getClass();
            Object proceed = proceedingJoinPoint.proceed();
            List<Object> allArgs = Arrays.asList(proceedingJoinPoint.getArgs());
            List<Object> args = allArgs.stream().map(arg -> {
                if (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)) {
                    return arg;
                } else {
                    return null;
                }
            }).filter(arg -> arg != null).collect(Collectors.toList());

            InformResp resp = new InformResp(requestURI, requestMethod, startTime,
                    getMethodArgumentTypeName(method), args, proceed != null ? proceed.toString() : "null",
                    (System.currentTimeMillis() - startTime) + "ms", currentClass.getName() + "." + method.getName(),
                    "0:0:0:0:0:0:0:1".equals(remoteAddr)? "127.0.0.1" :remoteAddr, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), method.getReturnType().getName());

        log.info("调用接口相关信息打印：{}",resp.toString());
            return proceed;
        } catch (Throwable e) {
            //抛出异常，不然全局异常处理器捕获不到异常
            throw e;
        }
    }

    private Map<String, String> getMethodArgumentTypeName(MethodSignature method) {
        Map<String, String> map = new HashMap<>();
        String[] argTypeNames = method.getParameterNames();
        Class[] parameterTypes = method.getParameterTypes();
        for (int i = 0; i < parameterTypes.length; i++) {
            map.put(parameterTypes[i].getName(), argTypeNames[i]);
        }
        return map;
    }

    private AuditLog getAnnotationLog(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        return method != null ? (AuditLog) method.getAnnotation(AuditLog.class) : null;
    }

    private void handleBeforeLog(JoinPoint joinPoint) {
        try {
            AuditLog controllerLog = this.getAnnotationLog(joinPoint);
            if (controllerLog == null) {
                return;
            }
            log.info("before ----> title:{}, desc:{}", controllerLog.title(), controllerLog.desc());
        } catch (Exception e) {
            MyHandlerExceptionResolver.formatException(e);
            log.error("handleBeforeLog methods is error: {}", e);
        }
    }

    protected void handleAfterLog(JoinPoint joinPoint) {
        try {
            AuditLog controllerLog = this.getAnnotationLog(joinPoint);
            if (controllerLog == null) {
                return;
            }
            log.info("after ----> title:{}, desc:{}", controllerLog.title(), controllerLog.desc());
        } catch (Exception e) {
            MyHandlerExceptionResolver.formatException(e);
            log.error("handleAfterLog methods is error: {}", e);
        }
    }

    protected void handleAfterThrowingLog(JoinPoint joinPoint, Exception e) {
        try {
            AuditLog controllerLog = this.getAnnotationLog(joinPoint);
            if (controllerLog == null) {
                return;
            }
            MyHandlerExceptionResolver.formatException(e);
            log.error("methods is error: {}", e);
        } catch (Exception ex) {
            MyHandlerExceptionResolver.formatException(ex);
            log.error("handleAfterThrowingLog methods is error: {}", ex);
        }
    }

}


